/**
 * 
 */
package com.sj.szxy.genetic.algorithm.basic;

/**
 * @author Yi Ping
 * @date 2018年4月3日 下午6:45:01 
 * @since 1.0.0
 *
 */
public class GeneticAlgorithm {
	
	private  int populationSize;
	private double mutationRate;
	private double crossoverRate;
	private int elitismCount;
	
	
	/**
	 * @param populationSize
	 * @param mutationRate
	 * @param crossoverRate
	 * @param elitismCount
	 */
	public GeneticAlgorithm(int populationSize, double mutationRate, double crossoverRate, int elitismCount) {
		this.populationSize = populationSize;
		this.mutationRate = mutationRate;
		this.crossoverRate = crossoverRate;
		this.elitismCount = elitismCount;
	}
	

	/**
	 * @param i
	 * @param d
	 * @param e
	 * @param j
	 * @param k
	 */
	public GeneticAlgorithm(int i, double d, double e, int j, int k) {
		// TODO Auto-generated constructor stub
	}

	/**
	 * 初始化种群
	 * @param chromosomeLength
	 * @return
	 */
	public Population initPopulation(int chromosomeLength) {
		Population population = new Population(this.populationSize, chromosomeLength);
		return population;
	}
	
	/**
	 * 计算个体的适用度
	 * @param individual
	 * @return
	 */
	public double calcFitness(Individual individual) {
		int correctGenes = 0;
		for(int i = 0; i <individual.getChromosomeLength(); i++) {
			if(individual.getGene(i)==1)
				correctGenes +=1;
		}
		
		double fitness = (double) correctGenes /individual.getChromosomeLength();
		individual.setFitness(fitness);
		return fitness;
	}
	
	/**
	 * 评估种群的适用度
	 * @param population
	 */
	public void evalPopulation(Population population) {
		double populationFitness = 0;
		for(Individual individual : population.getIndividuals()) {
			populationFitness +=calcFitness(individual);
		}
		
		population.setPopulationFitness(populationFitness);
	}
	
	/**
	 * 终止条件
	 * @param population
	 * @return
	 */
	public boolean isTerminationConditionMet(Population population) {
		for(Individual individual:population.getIndividuals()) {
			if(individual.getFitness() ==1)
				return true;
		}
		
		return false;
	}
	
	/**
	 * 采用轮盘赌选择父本
	 * @param population
	 * @return
	 */
	public Individual selectParent(Population population) {
		Individual individuals[] = population.getIndividuals();
		double populationFitness = population.getPopulationFitness();
		double rouletteWhellPosition = Math.random() * populationFitness;
		double spinWheel = 0;
		for(Individual individual: individuals) {
			spinWheel += individual.getFitness();
			if(spinWheel>=rouletteWhellPosition)
				return individual;
		}
		
		return individuals[population.size()-1];
	}
	
	/**
	 * 交叉产生下一代群体
	 * @param population
	 * @return
	 */
	public Population crossoverPopulation(Population population) {
		Population newPopulatio = new Population(population.size());
		for(int i=0; i<population.size(); i++) {
			Individual parent1= population.getFittest(i);
			// Apply crossover to this individual?
			if(this.crossoverRate > Math.random() && i > this.elitismCount) {
				Individual offspring = new Individual(parent1.getChromosomeLength());
				Individual parent2 = selectParent(population);
				for(int geneIndex = 0; geneIndex < parent1.getChromosomeLength(); geneIndex++) {
					//Use half of parent1's genes and of parent2's genes
					if(0.5< Math.random())
						offspring.setGene(geneIndex, parent1.getGene(geneIndex));
					else
						offspring.setGene(geneIndex, parent2.getGene(geneIndex));
				}
				newPopulatio.setIndividual(i, offspring);
			}else {
				// Add individual to new populatiohn without applying crossover
				newPopulatio.setIndividual(i, parent1);
			}
		}
		
		return newPopulatio;
	}
	
	/**
	 * 变异
	 * @param population
	 * @return
	 */
	public Population mutatePopulation(Population population) {
		Population newPopulation = new Population(this.populationSize);
		// Loop over current population by fitness
		for(int populationIndex =0; populationIndex< population.size(); populationIndex++) {
			Individual individual = population.getFittest(populationIndex);
			// Loop over individual's genes
			for(int geneIndex=0; geneIndex<individual.getChromosomeLength(); geneIndex++) {
				if(populationIndex>=this.elitismCount) {
					// Does this gene need mutation?
					if(this.mutationRate > Math.random()) {
						int newGene =1;
						if(individual.getGene(geneIndex) ==1) {
							newGene =0;
						}
						individual.setGene(geneIndex, newGene);
					}
				}
			}
			newPopulation.setIndividual(populationIndex, individual);
		}
		
		return newPopulation;
	}
}
